Wertlose Codeteile

im Projekt "Spring PetClinic"

Teil 1: Nutzungsstatistiken

Nutzungsdaten vom Produktivbetrieb


In [1]:
import pandas as pd
coverage = pd.read_csv("../dataset/jacoco_demo.csv")
coverage.head()


Out[1]:
PACKAGE CLASS LINE_MISSED LINE_COVERED
0 org.springframework.samples.petclinic PetclinicInitializer 0 24
1 org.springframework.samples.petclinic.model NamedEntity 1 4
2 org.springframework.samples.petclinic.model Specialty 0 1
3 org.springframework.samples.petclinic.model PetType 0 1
4 org.springframework.samples.petclinic.model Vets 4 0

Berechnung wesentlicher Metriken für Größe und Nutzungsgrad


In [2]:
coverage['lines'] = coverage.LINE_MISSED + coverage.LINE_COVERED
coverage['covered'] = coverage.LINE_COVERED / coverage.lines
coverage.head()


Out[2]:
PACKAGE CLASS LINE_MISSED LINE_COVERED lines covered
0 org.springframework.samples.petclinic PetclinicInitializer 0 24 24 1.0
1 org.springframework.samples.petclinic.model NamedEntity 1 4 5 0.8
2 org.springframework.samples.petclinic.model Specialty 0 1 1 1.0
3 org.springframework.samples.petclinic.model PetType 0 1 1 1.0
4 org.springframework.samples.petclinic.model Vets 4 0 4 0.0

Überblick über die Gesamtausnutzung der Softwareeinheiten


In [3]:
%matplotlib inline
import matplotlib.pyplot as plt
ax = coverage.covered.hist();
ax.set_title("Verteilung der Gesamtausnutzung")
ax.set_xlabel("Anzahl")
ax.set_ylabel("Nutzungsgrad");


Vorbereitung Verbindung zu technischen Schulden

  • Es wird ein eindeutiger Schlüssel ("fqn") für die Softwareinheiten erstellt, um nachfolgend Nutzungsdaten zu den technischen Schulden zuordnen zu können
  • Zudem werden nicht mehr benötigte Daten weggelassen

In [4]:
coverage['fqn'] = coverage.PACKAGE + "." + coverage.CLASS
coverage_per_class = coverage.set_index('fqn')[['lines', 'covered']]
coverage_per_class.head()


Out[4]:
lines covered
fqn
org.springframework.samples.petclinic.PetclinicInitializer 24 1.0
org.springframework.samples.petclinic.model.NamedEntity 5 0.8
org.springframework.samples.petclinic.model.Specialty 1 1.0
org.springframework.samples.petclinic.model.PetType 1 1.0
org.springframework.samples.petclinic.model.Vets 4 0.0

Teil 2: Technische Schulden

Laden der aktuellen Daten vom Qualitätssicherungsserver

Die aktuellen Messergebnisse der technischen Schulden der Anwendung werden geladen.


In [5]:
import requests
KEY = "org.springframework.samples:spring-petclinic:boundedcontexts"
URL = "https://sonarcloud.io/api/issues/search?languages=java&componentKeys="
issues_json = requests.get(URL + KEY).json()
print(str(issues_json)[:500])


{'total': 55, 'p': 1, 'ps': 100, 'paging': {'pageIndex': 1, 'pageSize': 100, 'total': 55}, 'effortTotal': 359, 'debtTotal': 359, 'issues': [{'key': 'AWLJtvMj-pl6AHs2EogL', 'rule': 'squid:S3008', 'severity': 'MINOR', 'component': 'org.springframework.samples:spring-petclinic:boundedcontexts:src/main/java/org/springframework/samples/petclinic/util/BrokenSingleton.java', 'project': 'org.springframework.samples:spring-petclinic:boundedcontexts', 'line': 5, 'hash': '48c1dff4bc3cd8f9b9b8c76f49484c2a',

Aufstellung der notwendigen Daten für tiefergehende Analysen


In [6]:
from pandas.io.json import json_normalize
issues = json_normalize(issues_json['issues'])[['component', 'debt']]
issues['debt'] = issues.debt.apply(pd.Timedelta)
issues.head()


Out[6]:
component debt
0 org.springframework.samples:spring-petclinic:b... 00:02:00
1 org.springframework.samples:spring-petclinic:b... 00:20:00
2 org.springframework.samples:spring-petclinic:b... 00:10:00
3 org.springframework.samples:spring-petclinic:b... 00:05:00
4 org.springframework.samples:spring-petclinic:b... 00:15:00

Vorbereitung der Zuordnung zu Auslastungsdaten

  • Es wird ein eindeutiger Schlüssel für die Softwareinheiten erstellt, um technischen Schulden zu Nutzungsdaten zuordnen zu können
  • Mehrfacheinträge zu technischen Schulden werden pro Softwareeinheit aufsummiert

In [7]:
issues['fqn'] = issues.component.str.extract("/java/(.*).java", expand=True)
issues['fqn'] = issues.fqn.str.replace("/", ".")
debt_per_class = issues.groupby('fqn')[['debt']].sum()
debt_per_class.head()


Out[7]:
debt
fqn
org.springframework.samples.petclinic.PetclinicInitializer 00:04:00
org.springframework.samples.petclinic.model.Pet 00:02:00
org.springframework.samples.petclinic.model.Vets 00:10:00
org.springframework.samples.petclinic.repository.OwnerRepository 00:15:00
org.springframework.samples.petclinic.repository.PetRepository 00:15:00

Teil 3: Management-Sicht

...ein paar Code-Zeilen weiter...

Zusammenführung der Daten

Anhand des vollqualifizierenden Klassennamens ("fqn") werden beide Datenquellen zusammengeführt.


In [8]:
analysis = coverage_per_class.join(debt_per_class)
analysis = analysis.fillna(0)
analysis.head()


Out[8]:
lines covered debt
fqn
org.springframework.samples.petclinic.PetclinicInitializer 24 1.0 00:04:00
org.springframework.samples.petclinic.model.NamedEntity 5 0.8 00:00:00
org.springframework.samples.petclinic.model.Specialty 1 1.0 00:00:00
org.springframework.samples.petclinic.model.PetType 1 1.0 00:00:00
org.springframework.samples.petclinic.model.Vets 4 0.0 00:10:00

Markierung fachlicher Komponenten


In [9]:
analysis['domain'] = "Other" # Standardvorbelegung
domains = ["Owner", "Pet", "Visit", "Vet", "Specialty", "Clinic"]
for domain in domains:
    analysis.loc[analysis.index.str.contains(domain), 'domain'] = domain
analysis.head()


Out[9]:
lines covered debt domain
fqn
org.springframework.samples.petclinic.PetclinicInitializer 24 1.0 00:04:00 Pet
org.springframework.samples.petclinic.model.NamedEntity 5 0.8 00:00:00 Other
org.springframework.samples.petclinic.model.Specialty 1 1.0 00:00:00 Specialty
org.springframework.samples.petclinic.model.PetType 1 1.0 00:00:00 Pet
org.springframework.samples.petclinic.model.Vets 4 0.0 00:10:00 Vet

Nutzungsgrad und technische Schulden nach Fachlichkeit


In [10]:
mgt = analysis.groupby('domain').agg(
    {"covered" : "mean", "debt" : "sum", "lines" : "sum"})
mgt['debt'] = mgt['debt'].dt.seconds / 60
mgt.columns = ['Nutzungsgrad (%)', 'Technische Schulden (min)', 'Größe']
mgt


Out[10]:
Nutzungsgrad (%) Technische Schulden (min) Größe
domain
Clinic 0.888889 40.0 18
Other 0.640278 55.0 49
Owner 0.549495 65.0 130
Pet 0.594156 30.0 153
Specialty 1.000000 0.0 1
Vet 0.176667 15.0 45
Visit 0.385417 7.0 77

Bewertungsmatrix nach Fachlichkeit


In [11]:
from ausi import portfolio

In [12]:
portfolio.plot_diagram(mgt, "Technische Schulden (min)", "Nutzungsgrad (%)", "Größe");


Zusammenfassung

Erkenntnisse

  • Die Investitionen in Kernfunktionalität ("Pet") gut.
  • Risiko besteht bei den "sonstigen Modulen" ("Other")

Maßnahme: Für die Komponente "Other" müssen dringends qualitätsverbessernde Maßnahmen ergriffen werden

Anhang

Nutzung nach technischen Komponenten

Nutzungsgrad und technische Schulden nach technischen Komponenten


In [13]:
analysis['tech'] = analysis.index.str.split(".").str[-2]
mgt = analysis.groupby('tech').agg(
    {"covered": "mean", "debt" : "sum", "lines" : "sum"})
mgt['debt'] = mgt['debt'].dt.seconds / 60
mgt.columns = ['Nutzungsgrad (%)', 'Technische Schulden (min)', 'Größe']
mgt


Out[13]:
Nutzungsgrad (%) Technische Schulden (min) Größe
tech
jdbc 0.000000 56.0 150
jpa 0.691558 0.0 28
model 0.739048 12.0 108
petclinic 1.000000 4.0 24
service 0.888889 40.0 18
util 0.270833 35.0 30
web 0.639809 65.0 115

Nutzungsgrad nach technischen Funktionen


In [14]:
portfolio.plot_diagram(mgt, "Technische Schulden (min)", "Nutzungsgrad (%)", "Größe");


Ende